/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package codegen;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import parser.nodes.CompileError;
import parser.nodes.Node;

public class CodeGen {

    private int level = 0;
    public static boolean debug = false;
    private BufferedWriter out = null;
    private List<Map<String, AbstractDescr>> symbolTables = null;
    private String filename;
    private int currentLabel = 0;

    public CodeGen(String filename) {
        this.filename = filename;
    }

    public void outstr(String what) {
        try {
            out.write(what + "\n");
        } catch (IOException e) {
            System.err.println("Exception ocurred: " + e);
        }
    }

    public void compile(Node syntaxTree) throws IOException, CompileError {
        try (BufferedWriter temp = new BufferedWriter(new FileWriter(filename))) {
            out = temp;
            symbolTables = new ArrayList<>();
            level = -1;
            levelUp();
            putDescr("integer", new SimpleTypeDescr("integer"));
			syntaxTree.compile(this);
            System.out.println(symbolTables.get(level));
            symbolTables = null;
            out = null;
        }
    }

    public AbstractDescr findDescr(String name) {
        AbstractDescr result;
        for (int c = level; c >= 0; --c) {
            result = symbolTables.get(c).get(name);
            if (result != null) {
                return result;
            }
        }
        return null;
    }
    
    public boolean currentLevelContains(String name) {
        return symbolTables.get(level).containsKey(name);
    }

    public void putDescr(String name, AbstractDescr descriptor) {
        if (!currentLevelContains(name))
            symbolTables.get(level).put(name, descriptor);
        else
            System.err.println("The name " + name + " has multiple defninitions");
    }

    public void levelUp() {
        ++level;
        symbolTables.add(new HashMap<String, AbstractDescr>());
    }

    public Map<String, AbstractDescr> levelDown() {
        return symbolTables.remove(level--);
    }

    public int getLabel() {
        return currentLabel++;
    }

    public int getLevel() {
        return level + 1;
    }
    
    public int nextFreeAddress() {
        int addr = 0;
        for(AbstractDescr desc : symbolTables.get(level).values()) {
            if (!(desc instanceof SimpleTypeDescr) && !(desc instanceof TypeDescr) && !(desc instanceof ParamDescr)) //Parameter haben negative adressen
                addr += desc.getSize();
        }
        return addr;
    }
}
